"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; interface OutputFile { filePath: string; fileSizeBytes: number; width: number; height: number; durationSeconds: number; } interface Job { id: string; status: "pending" | "running" | "completed" | "failed"; input: { text: string; template: string; platform: string; ttsProvider: string; }; createdAt: number; updatedAt: number; error?: string; outputFiles?: OutputFile[]; } const STATUS_BADGE: Record = { pending: "badge-pending", running: "badge-running", completed: "badge-completed", failed: "badge-failed", }; const STATUS_LABEL: Record = { pending: "Waiting", running: "Rendering...", completed: "Completed", failed: "Failed", }; export default function JobDetailPage({ params }: { params: Promise<{ id: string }> }) { const [job, setJob] = useState(null); const [loading, setLoading] = useState(true); const [id, setId] = useState(""); useEffect(() => { params.then((p) => setId(p.id)); }, [params]); useEffect(() => { if (!id) return; const load = () => { fetch(`/api/jobs/${id}`) .then((r) => r.json()) .then((data: Job) => { setJob(data); setLoading(false); if (data.status === "pending" || data.status === "running") { setTimeout(load, 2000); } }) .catch(() => setLoading(false)); }; load(); }, [id]); if (loading) { return (
Loading...
); } if (!job) { return (

Job not found

); } return (
Jobs /

{job.id.slice(0, 8)}

{job.status === "running" && ( )} {STATUS_LABEL[job.status]} {new Date(job.createdAt).toLocaleString("zh-CN")}
{/* Config cards */}
Template
{job.input.template}
Platform
{job.input.platform}
TTS Provider
{job.input.ttsProvider}
Input
JSON ({job.input.text.length} chars)
{/* Error */} {job.error && (
Error
{job.error}
)} {/* Output files */} {job.status === "completed" && job.outputFiles && job.outputFiles.length > 0 && (

Output

{job.outputFiles.map((file, i) => (
{/* Video player */}
))}
)} {/* Input text */}
Input Text
          {job.input.text.slice(0, 1000)}{job.input.text.length > 1000 ? "..." : ""}
        
); }